/*
 * @brief: v1.0 电量读取还没写。
 * @Author: TzzzT
 * @Date: 2022-03-06 01:08:44
 * @LastEditTime: 2023-04-16 22:39:21
 * @LastEditors: TzzzT
 */
#include "stdint.h"
#include "string.h"
#include "driver/rmt_rx.h"
#include "driver/adc.h"
#include "esp_adc_cal.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/queue.h"

#include "my_crc.h"

#include "RCS_protocol.h"
#include "RCS_slave.h"
#include "RCS_bullet.h"
#include "RCS_fire.h"
#include "rcs_api.h"

#define OK 1
#define FAIL 0

static const char *TAG = "IR_TAG";

////////////////////////////RMT RX////////////////////////////

extern QueueHandle_t xUploadDataQueue;
extern TaskHandle_t shockTaskHandle;
extern rcs_slave_system_t rcsSystem;

static void remote_handle(uint8_t command);
static void bullet_data_handle(bool is_in_fix_mode, uint8_t array[], size_t length);
////////// RMT MODULE //////////////
#define RMT_RX_PIN GPIO_NUM_4
#define IR_RESOLUTION_HZ 1000000 // 1MHz resolution, 1 tick = 1us

#define RMT_RX_BUFF_ITEM_SIZE 64
rmt_symbol_word_t raw_symbols[RMT_RX_BUFF_ITEM_SIZE]; // 64 symbols should be sufficient for a standard NEC frame
rmt_rx_done_event_data_t rx_data;
rmt_channel_handle_t rx_channel = NULL;
QueueHandle_t rmt_rx_queue;
// the following timing requirement is based on NEC protocol
rmt_receive_config_t receive_config = {
    .signal_range_min_ns = 1250,     // the shortest duration for NEC signal is 560us, 1250ns < 560us, valid signal won't be treated as noise
    .signal_range_max_ns = 12000000, // the longest duration for NEC signal is 9000us, 12000000ns > 9000us, the receive won't stop early
};

static bool example_rmt_rx_done_callback(rmt_channel_handle_t channel, const rmt_rx_done_event_data_t *edata, void *user_data);

rmt_rx_event_callbacks_t cbs = {
    .on_recv_done = example_rmt_rx_done_callback,
};

void rcs_rmt_rx_init(void)
{
    rmt_rx_channel_config_t rx_channel_cfg = {
        .clk_src = RMT_CLK_SRC_DEFAULT,
        .resolution_hz = IR_RESOLUTION_HZ,
        .mem_block_symbols = RMT_RX_BUFF_ITEM_SIZE, // amount of RMT symbols that the channel can store at a time
        .gpio_num = RMT_RX_PIN,
        .flags.invert_in = 0, // 设置0和1都没区别
    };
    ESP_ERROR_CHECK(rmt_new_rx_channel(&rx_channel_cfg, &rx_channel));
    rmt_carrier_config_t carrier_cfg = {
        .duty_cycle = 0.33,
        .frequency_hz = 38000, // 38KHz
    };
    ESP_ERROR_CHECK(rmt_apply_carrier(rx_channel, &carrier_cfg));

    ESP_LOGI(TAG, "register RX done callback");
    rmt_rx_queue = xQueueCreate(1, sizeof(rmt_rx_done_event_data_t));
    assert(rmt_rx_queue);
    ESP_ERROR_CHECK(rmt_rx_register_event_callbacks(rx_channel, &cbs, rmt_rx_queue));
    ESP_ERROR_CHECK(rmt_enable(rx_channel));
}

static void rcs_rmt_parse_frame(rmt_symbol_word_t *rmt_nec_symbols, size_t symbol_num);

void rcs_ir_rx_task(void *arg)
{
    rcs_rmt_rx_init();
    while (1)
    {
        ESP_ERROR_CHECK(rmt_receive(rx_channel, raw_symbols, sizeof(raw_symbols), &receive_config));
        // wait for RX done signal
        if (xQueueReceive(rmt_rx_queue, &rx_data, pdMS_TO_TICKS(1000)) == pdPASS)
        {
            // parse the receive symbols and print the result
            if (rx_data.num_symbols)
            {
                rcs_rmt_parse_frame(rx_data.received_symbols, rx_data.num_symbols);
            }
        }
    }
}

/**
 * @brief Decode RMT symbols into NEC scan code and print the result
 */
static void rcs_rmt_parse_frame(rmt_symbol_word_t *rmt_nec_symbols, size_t symbol_num)
{
    static uint8_t last_command;
    ESP_LOGI(TAG, "symbol_num: %d", symbol_num);

    switch (symbol_num)
    {
    case NEC_CLASS_NUM:
        ESP_LOGI(TAG, "receive NEC");
        // for (size_t i = 0; i < symbol_num; i++)
        // {
        //     printf("{%d:%d},{%d:%d}\r\n", rmt_nec_symbols[i].level0, rmt_nec_symbols[i].duration0,
        //            rmt_nec_symbols[i].level1, rmt_nec_symbols[i].duration1);
        // }
        if (nec_parse_frame(rmt_nec_symbols, &last_command))
        {
            ESP_LOGI(TAG, "receive remote command: %d", last_command); // dummydata应该是4字节的数组，下标2成员是数据
            remote_handle(last_command);
        }
        break;
    case NEC_REPEAT_CLASS_NUM:
    {
        // NEC repeat frame
        if (nec_parse_frame_repeat(rmt_nec_symbols))
        {
            ESP_LOGI(TAG, "receive repeat command: %d", last_command); // dummydata应该是4字节的数组，下标2成员是数据
        }
        break;
    }
    case CUSTOM_CLASS_NUM:
    {
        uint8_t result[BULLET_DATA_LEN] = {0};
        if (custom_parse_frame(rmt_nec_symbols, result))
        {
            ESP_LOGI(TAG, "receive a bullet data");
            bullet_data_handle(false, result, sizeof(result));
        }
        break;
    }
    case FIX_BULLET_NUM:
    {
        ESP_LOGI(TAG, "receive a fix bullet");
        uint8_t result[BULLET_DATA_LEN];
        if (fix_bullet_parse_frame(rmt_nec_symbols, result))
        {
            ESP_LOGI(TAG, "receive a fix bullet data");
            bullet_data_handle(true, result, sizeof(result));
        }
    }
    default:
        break;
    }
}

static bool example_rmt_rx_done_callback(rmt_channel_handle_t channel, const rmt_rx_done_event_data_t *edata, void *user_data)
{
    BaseType_t high_task_wakeup = pdFALSE;
    QueueHandle_t rmt_receive_queue = (QueueHandle_t)user_data;
    // send the received RMT symbols to the parser task
    xQueueSendFromISR(rmt_receive_queue, edata, &high_task_wakeup);
    return high_task_wakeup == pdTRUE;
}

/**
 * @brief: 遥控器指令的处理函数，把解析的指令按照协议的格式打包，放入红外队列，设置eventbit
 * @param  {*}
 * @return {*}
 * @Author: TzzzT
 * @Date: 2022-04-20 00:23:55
 * @LastEditTime: Do not edit
 * @LastEditors: Shadow
 * @param {uint8_t} command
 */
static void remote_handle(uint8_t command)
{
    uint8_t package_len = 0;
    uint8_t *package = make_command_package(command, &package_len);
    set_game_event(REMOTE_COMM_EVENT, 4);
    ESP_LOGI(TAG, "put remote_command_package in queue");
    if (xQueueSend(xUploadDataQueue, package, pdMS_TO_TICKS(200)) != pdPASS)
    {
        ESP_LOGI(TAG, "queue full.");
    }
    free(package);
}

static uint8_t bullet_data_parse(bool is_in_fix_mode, uint8_t *buffer, player_t *enemy);
/**
 * @brief: 子弹数据的处理函数，把解析的数据按照协议的格式打包，放入红外队列，设置eventbit，唤醒震动任务
 * 子弹数据格式: 房间_队伍 | 伤害值 | 玩家ID(3 Bytes) | CRC16(2 Bytes)
 * @param  {*}
 * @return {*}
 * @Author: TzzzT
 * @Date: 2022-04-20 00:26:10
 * @LastEditTime: Do not edit
 * @LastEditors: Shadow
 * @param {uint8_t} array
 * @param {size_t} length
 */
static void bullet_data_handle(bool is_in_fix_mode, uint8_t *array, size_t length)
{
    if (!rcsSystem.game.start)
    {
        ESP_LOGE(TAG, "game no start, bullet no effection.");
        return;
    }
    player_t enemy = {0};
    ESP_LOGI(TAG, "bullet rx");
    bullet_data_parse(is_in_fix_mode, array, &enemy);
    ESP_LOGI(TAG, "Attack from: team :%d, room :%d, damage :%d, ID :%" PRIu32 " .\n",
             enemy.team, enemy.room, enemy.damage, enemy.id);
    uint8_t *package = make_be_hitted_package(&enemy, rcsSystem.device.battery);
    ESP_LOGI(TAG, "put bullet_data_package in queue");
    if (xQueueSend(xUploadDataQueue, package, pdMS_TO_TICKS(200)) != pdPASS)
    {
        ESP_LOGE(TAG, "queue full.");
    }
    free(package);
    set_game_event(BE_HITTED, 8);
    eTaskState state = eTaskGetState(shockTaskHandle);
    if (state == eSuspended)
    {
        ESP_LOGE(TAG, "vTaskResume( shockTaskHandle )");
        vTaskResume(shockTaskHandle); // 被击中以后，唤醒震动任务
    }
}

static uint8_t bullet_data_parse(bool is_in_fix_mode, uint8_t *buffer, player_t *enemy)
{
    if (is_in_fix_mode)
    {
        fix_bullet_t bu;
        memcpy(bu.array, buffer, sizeof(bu.array));
        // in fix bullet mode.
        enemy->team = bu.data.team;
        // enemy->damage = bu.data.damage;
        enemy->id = 111;
        enemy->damage = 30;
    }
    else
    {
        if (buffer == NULL || enemy == NULL)
        {
            return FAIL;
        }
        enemy->team = buffer[TEAM_ROOM] / 10;
        enemy->room = buffer[TEAM_ROOM] % 10;
        enemy->damage = buffer[DAMAGE] * 10;
        enemy->id = buffer[PLAYER_ID_1] << 16;
        enemy->id |= buffer[PLAYER_ID_2] << 8;
        enemy->id |= buffer[PLAYER_ID_3];
    }

    return OK;
}
